package com.hxh.generater.springBoot.simple.gen.crud;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.hxh.config.Constants;
import com.hxh.entity.Project;
import com.hxh.entity.ProjectTableFiledConfig;
import com.hxh.entity.po.ClassInfo;
import com.hxh.entity.po.FieldName;
import com.hxh.generater.springBoot.util.ProcessWriteLine;
import com.hxh.generater.springBoot.util.methodParse.ParseMybatisXmlMethod;
import com.hxh.generater.springBoot.util.methodParse.entity.MethodBase;
import com.hxh.util.GenUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.io.*;
import java.util.*;

/**
 * @ProjectName: AICode
 * @Package: com.hxh.generater
 * @Author: HXH
 * @Description:
 * @Date: 2023/11/28 15:08
 */
@Slf4j
public class GenMapper {
    public static void generationMapper(Project project, ClassInfo classInfo) throws IOException {
        generationMapper(project, classInfo, null);
    }

    public static String generationMapper(String packageName, ClassInfo classInfo) throws IOException {
        return generationMapper(null, classInfo, packageName);
    }

    /**
     * @param project:
     * @param classInfo:
     * @return void
     * @author HXH
     * @description: 规则：
     * 自动处理字段create_time，update_time
     * 自增id自动处理
     * textarea / text 类型的字段，模糊匹配
     * checkbox字段自动处理
     * @date 2024/5/22
     **/
    private static String generationMapper(Project project, ClassInfo classInfo, String packageName) throws IOException {
        if (project == null && StringUtils.isBlank(packageName)) {
            log.error("generationMapper中，project参数和packageName参数不能同时为空!");
            throw new RuntimeException("generationMapper中，project参数和packageName参数不能同时为空!");
        }
        boolean returnModel = false;
        String projectGroup;

        if (project == null) {
            returnModel = true;
            projectGroup = packageName;
        } else {
            projectGroup = project.getBackendProject().getProjectGroup();
        }


        String className = classInfo.getClassName();

        String daoName = className + "Dao";
        String daoPath = projectGroup + ".dao." + daoName;
        String entityPath = projectGroup + ".entity." + className;

        String tableName = classInfo.getTableName();
        String mapperName = className + "Mapper";

        String mapperTempName = mapperName + ".Template";

        File tempClassFile;
        if (returnModel) {
            tempClassFile = File.createTempFile(daoName, ".Template");
        } else {
            tempClassFile = GenUtil.newFile(project, "mapper", mapperTempName);
        }

        //先生成模板
        try {
            FileWriter fileWriter = new FileWriter(tempClassFile);
            BufferedWriter writer = new BufferedWriter(fileWriter);

            File template = new File(Constants.templatePathPrefix + "simple\\template\\resources\\mapper");
            BufferedReader br = new BufferedReader(new FileReader(template));
            String line;
            while ((line = br.readLine()) != null) {
                if (StringUtils.isNotBlank(line)) {
                    writer.write(line);
                }
                writer.newLine();
            }
            br.close();

            //加上addition\\mapperTemplateAddition文件下，与类名相同的文件夹下所有的文件
            File additionFile = new File(Constants.templatePathPrefix + "simple\\template\\addition\\mapperTemplateAddition\\" + mapperName);
            if (additionFile.exists() && additionFile.isDirectory()) {
                File[] additionFiles = additionFile.listFiles();
                if (additionFiles != null && additionFiles.length > 0) {
                    for (File file : additionFiles) {
                        br = new BufferedReader(new FileReader(file));
                        while ((line = br.readLine()) != null) {
                            if (StringUtils.isNotBlank(line)) {
                                writer.write(line);
                            }
                            writer.newLine();
                        }
                        br.close();
                    }
                }
            }

            writer.newLine();
            writer.write("</mapper>");

            writer.close();
            fileWriter.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {

            BufferedWriter writer;
            FileWriter fileWriter = null;
            StringWriter stringWriter = null;
            if (returnModel) {
                stringWriter = new StringWriter();
                writer = new BufferedWriter(stringWriter);
            } else {
                File classFile = GenUtil.newFile(project, "mapper", mapperName);
                fileWriter = new FileWriter(classFile);
                writer = new BufferedWriter(fileWriter);
            }

            File template = tempClassFile;
            BufferedReader br = new BufferedReader(new FileReader(template));
            String line;

            StringBuilder resultMapIdColumn = new StringBuilder();
            StringBuilder resultMapOtherColumn = new StringBuilder();
            StringBuilder resultMapAssociationColumn = new StringBuilder();
            StringBuilder additionMapper = new StringBuilder();
            StringBuilder sqlAllColumn = new StringBuilder();
            StringBuilder sqlAllColumnNoAutoId = new StringBuilder();
            StringBuilder sqlAllColumnAutoId = new StringBuilder();
            StringBuilder insertListAutIncrementValue = new StringBuilder();
            String allColumnAutoId = "";

            StringBuilder insertColumnKeys = new StringBuilder();
            StringBuilder insertColumnValues = new StringBuilder();
            StringBuilder insertListValues = new StringBuilder();
            StringBuilder selectKeysAndValues = new StringBuilder();
            StringBuilder selectByIdList = new StringBuilder();
            StringBuilder updateKeysAndValues = new StringBuilder();
            StringBuilder updateKeysAndValuesSelective = new StringBuilder();
            StringBuilder updateByIdList = new StringBuilder();

            StringBuilder autoSetIdAfterInsert = new StringBuilder();

            StringBuilder listPageOrderBy = new StringBuilder();

            Set<String> additionMapperSet = new HashSet<>();

            for (FieldName fieldName : classInfo.getFieldNameList()) {
                if (Constants.MYSQL_PRIMARY_KEY_NAME.equals(fieldName.getKey())) {
                    resultMapIdColumn.append("        <id column=\"" + fieldName.getName() + "\" property=\"" + fieldName.getJavaName() + "\"/>\n");
                    selectByIdList.append("`" + fieldName.getName() + "` = #{" + fieldName.getJavaName() + "} and ");
                    updateByIdList.append("`" + fieldName.getName() + "` = #{pojo." + fieldName.getJavaName() + "} and ");

                    if (fieldName.isAutIncrement()) {
                        autoSetIdAfterInsert.append("useGeneratedKeys=\"true\" keyProperty=\"" + fieldName.getJavaName() + "\"");
                    }

                } else {
                    resultMapOtherColumn.append("        <result column=\"" + fieldName.getName() + "\" property=\"" + fieldName.getJavaName() + "\"/>\n");

                    if (GenUtil.AutoProcessUpdateTimeFields.equals(fieldName.getName())) {
                        updateKeysAndValues.append("            `" + fieldName.getName() + "` = now(),\n");
                        updateKeysAndValuesSelective.append("            `" + fieldName.getName() + "` = now(),\n");
                        if (listPageOrderBy.length() == 0) {
                            listPageOrderBy.append(" order by " + GenUtil.AutoProcessUpdateTimeFields + " desc");
                        } else {
                            listPageOrderBy.setLength(0);
                            listPageOrderBy.append(" order by " + GenUtil.AutoProcessUpdateTimeFields + " desc," + GenUtil.AutoProcessCreateTimeFields + " desc");
                        }
                    } else if (GenUtil.AutoProcessCreateTimeFields.equals(fieldName.getName())) {
                        if (listPageOrderBy.length() == 0) {
                            listPageOrderBy.append(" order by " + GenUtil.AutoProcessCreateTimeFields + " desc");
                        } else {
                            listPageOrderBy.setLength(0);
                            listPageOrderBy.append(" order by " + GenUtil.AutoProcessUpdateTimeFields + " desc," + GenUtil.AutoProcessCreateTimeFields + " desc");
                        }
                    } else {
                        updateKeysAndValuesSelective.append("            <if test=\"pojo." + fieldName.getJavaName() + " != null\">`" + fieldName.getName() + "` = #{pojo." + fieldName.getJavaName() + "},</if>\n");
                        updateKeysAndValues.append("            `" + fieldName.getName() + "` = #{pojo." + fieldName.getJavaName() + "},\n");
                    }

                }
                sqlAllColumn.append("`" + fieldName.getName() + "`,");

                if (!fieldName.isAutIncrement()) {
                    sqlAllColumnNoAutoId.append("`" + fieldName.getName() + "`,");
                } else {
                    //自动增长id应该只有一个
                    allColumnAutoId = fieldName.getName();
                }

                ProjectTableFiledConfig projectTableFiledConfig = fieldName.getProjectTableFiledConfig();

                if ("java.util.Date".equals(fieldName.getJavaType())) {
                    selectKeysAndValues.append("            <if test=\"pojo." + fieldName.getJavaName() + " != null\">AND `" + fieldName.getName() + "` = #{pojo." + fieldName.getJavaName() + "}</if>\n");
                } else if (projectTableFiledConfig != null) {
//                    text, textarea, number, radio, checkbox, date,dateTime, file, img association collection
                    String showType = projectTableFiledConfig.getShowType();
                    if ("association".equals(showType)) {
                        String associationTable = projectTableFiledConfig.getAssociationTable();
                        JSONObject jsonObject = new JSONObject(associationTable);
                        String aTableName = jsonObject.getStr("table");
                        String aFieldName = jsonObject.getStr("associationField");
                        aFieldName = StrUtil.toCamelCase(aFieldName);
                        aFieldName = StrUtil.upperFirst(aFieldName);
                        aTableName = StrUtil.toCamelCase(aTableName);
                        String aTableNameClassName = StrUtil.upperFirst(aTableName);

                        String selectId = "select" + aTableNameClassName + "By" + aFieldName;
                        String selectField;
                        String packageNameTemp = projectGroup + ".dao." + aTableNameClassName + "Dao";
                        if (jsonObject.getStr("showField") != null) {
                            selectField = "`" + jsonObject.getStr("showField") + "`";
                        } else {
                            selectField = "<include refid=\"" + packageNameTemp + ".all_column\"/>";
                        }
                        resultMapAssociationColumn.append("        <association property=\"" + aTableName + "\" column=\"" + fieldName.getName() + "\"  " +
                                " select=\"" + selectId + "\" />\n");
                        StringBuilder temp = new StringBuilder();
                        temp.append("    <select id=\"" + selectId + "\"  resultMap=\"" + packageNameTemp + ".BaseResultMap\">\n");
                        temp.append("        select " + selectField + " from `" + jsonObject.getStr("table") + "` where " + jsonObject.getStr("associationField") + " = #{0}\n");
                        temp.append("    </select>\n");
                        additionMapperSet.add(temp.toString());
                        selectKeysAndValues.append("            <if test=\"pojo." + fieldName.getJavaName() + " != null and pojo." + fieldName.getJavaName() + " != ''\">AND `" + fieldName.getName() + "` = #{pojo." + fieldName.getJavaName() + "}</if>\n");
                    } else if ("collection".equals(showType)) {
                        String collectionTable = projectTableFiledConfig.getCollectionTable();
                        JSONObject jsonObject = new JSONObject(collectionTable);
                        String collectionShowType = jsonObject.getStr("showType");
                        if ("data".equals(collectionShowType)) {
                            String aTableName = jsonObject.getStr("table");
                            String aFieldName = jsonObject.getStr("associationField");

                            aFieldName = StrUtil.toCamelCase(aFieldName);
                            aFieldName = StrUtil.upperFirst(aFieldName);
                            aTableName = StrUtil.toCamelCase(aTableName);
                            String aTableNameClassName = StrUtil.upperFirst(aTableName);
                            String selectName = projectGroup + ".dao." + aTableNameClassName + "Dao.listPage";
                            String ofType = projectGroup + ".entity." + aTableNameClassName;
                            String column = "{pojo." + aFieldName + "=" + fieldName.getJavaName() + "}";
                            resultMapAssociationColumn.append("        <collection property=\"" + aTableName + "List\" javaType=\"java.util.ArrayList\" ofType=\"" + ofType + "\" select=\"" + selectName + "\" column=\"" + column + "\" />\n");
                        }
                    } else if ("text".equals(showType) || "textarea".equals(showType)) {
                        selectKeysAndValues.append("            <if test=\"pojo." + fieldName.getJavaName() + " != null and pojo." + fieldName.getJavaName() + " != ''\">AND `" + fieldName.getName() + "` like CONCAT('%', #{pojo." + fieldName.getJavaName() + "},'%')</if>\n");
                    } else if ("checkbox".equals(showType)) {
                        selectKeysAndValues.append("            <if test=\"pojo." + fieldName.getJavaName() + " != null and pojo." + fieldName.getJavaName() + " != ''\">\n");
                        selectKeysAndValues.append("                <foreach item=\"item\" collection=\"pojo." + fieldName.getJavaName() + ".split(',')\" open=\"AND (\" close=\")\" separator=\" and \">\n");
                        selectKeysAndValues.append("                    CONCAT(',', `" + fieldName.getName() + "`,',') like CONCAT('%,', #{item},',%')\n");
                        selectKeysAndValues.append("                </foreach>\n");
                        selectKeysAndValues.append("            </if>\n");
                    } else {
                        selectKeysAndValues.append("            <if test=\"pojo." + fieldName.getJavaName() + " != null and pojo." + fieldName.getJavaName() + " != ''\">AND `" + fieldName.getName() + "` = #{pojo." + fieldName.getJavaName() + "}</if>\n");
                    }
                } else {
                    selectKeysAndValues.append("            <if test=\"pojo." + fieldName.getJavaName() + " != null and pojo." + fieldName.getJavaName() + " != ''\">AND `" + fieldName.getName() + "` = #{pojo." + fieldName.getJavaName() + "}</if>\n");
                }

                if (GenUtil.AutoProcessCreateTimeFields.equals(fieldName.getName()) || GenUtil.AutoProcessUpdateTimeFields.equals(fieldName.getName())) {
                    insertListValues.append("            now(),\n");
                } else if (fieldName.isAutIncrement()) {
                    insertListValues.append("#insertListAutIncrementValue#");
                } else {
                    insertListValues.append("            #{pojo." + fieldName.getJavaName() + "},\n");
                }

                if (GenUtil.AutoProcessCreateTimeFields.equals(fieldName.getName()) || GenUtil.AutoProcessUpdateTimeFields.equals(fieldName.getName())) {
                    insertColumnKeys.append("             " + fieldName.getName() + ",\n");
                } else {
                    insertColumnKeys.append("             <if test=\"" + fieldName.getJavaName() + " != null\">`" + fieldName.getName() + "`,</if>\n");
                }

                if (GenUtil.AutoProcessCreateTimeFields.equals(fieldName.getName()) || GenUtil.AutoProcessUpdateTimeFields.equals(fieldName.getName())) {
                    insertColumnValues.append("             now(),\n");
                } else {
                    insertColumnValues.append("             <if test=\"" + fieldName.getJavaName() + " != null\">#{" + fieldName.getJavaName() + "},</if>\n");
                }
            }

            if (StringUtils.isNotBlank(allColumnAutoId)) {
                sqlAllColumnAutoId.append("        <if test=\"pojos[0]." + allColumnAutoId + " != null \">\n" +
                        "            <include refid=\"all_column\"/>\n" +
                        "        </if>\n" +
                        "        <if test=\"pojos[0]." + allColumnAutoId + " == null\">\n" +
                        "            <include refid=\"all_column_no_auto_id\"/>\n" +
                        "        </if>\n");
                insertListAutIncrementValue.append("            <if test=\"pojos[0]." + allColumnAutoId + " != null\">\n" +
                        "                #{pojo." + allColumnAutoId + "},\n" +
                        "            </if>\n");
            } else {
                sqlAllColumnAutoId.append("            <include refid=\"all_column_no_auto_id\"/>\n");
            }

            if (sqlAllColumnNoAutoId.length() > 0) {
                sqlAllColumnNoAutoId.deleteCharAt(sqlAllColumnNoAutoId.length() - 1);
            }
            if (sqlAllColumn.length() > 0) {
                sqlAllColumn.deleteCharAt(sqlAllColumn.length() - 1);
                insertColumnKeys.deleteCharAt(insertColumnKeys.length() - 1);
                insertColumnValues.deleteCharAt(insertColumnValues.length() - 1);
                selectKeysAndValues.deleteCharAt(selectKeysAndValues.length() - 1);
                insertListValues.delete(insertListValues.length() - 2, insertListValues.length());
            }
            if (resultMapAssociationColumn.length() > 0) {
                resultMapOtherColumn.append(resultMapAssociationColumn);
            }
            if (resultMapOtherColumn.length() > 0) {
                resultMapOtherColumn.deleteCharAt(resultMapOtherColumn.length() - 1);
                updateKeysAndValues.deleteCharAt(updateKeysAndValues.length() - 1);
                updateKeysAndValuesSelective.deleteCharAt(updateKeysAndValuesSelective.length() - 1);
            }
            if (resultMapIdColumn.length() > 0) {
                resultMapIdColumn.deleteCharAt(resultMapIdColumn.length() - 1);
                selectByIdList.delete(selectByIdList.length() - 4, selectByIdList.length());
                updateByIdList.delete(updateByIdList.length() - 4, updateByIdList.length());
            }

            if (!CollectionUtils.isEmpty(additionMapperSet)) {
                additionMapperSet.forEach(additionMapper::append);
            }

            Map<String, String> replaceWord = new HashMap<>();

            replaceWord.put("insertListColumn", sqlAllColumnAutoId.toString());
            replaceWord.put("daoPath", daoPath);
            replaceWord.put("entityPath", entityPath);
            replaceWord.put("resultMapIdColumn", resultMapIdColumn.toString());
            replaceWord.put("resultMapOtherColumn", resultMapOtherColumn.toString());
            replaceWord.put("additionMapper", additionMapper.toString());
            replaceWord.put("sqlAllColumn", sqlAllColumn.toString());
            replaceWord.put("sqlAllColumnNoAutoId", sqlAllColumnNoAutoId.toString());
            replaceWord.put("insertColumnKeys", insertColumnKeys.toString());
            replaceWord.put("insertColumnValues", insertColumnValues.toString());
            replaceWord.put("tableName", tableName);
            replaceWord.put("insertListValues", insertListValues.toString());
            replaceWord.put("selectKeysAndValues", selectKeysAndValues.toString());
            replaceWord.put("selectByIdList", selectByIdList.toString());
            replaceWord.put("updateKeysAndValues", updateKeysAndValues.toString());
            replaceWord.put("updateKeysAndValuesSelective", updateKeysAndValuesSelective.toString());
            replaceWord.put("updateByIdList", updateByIdList.toString());
            replaceWord.put("listPageOrderBy", listPageOrderBy.toString());
            replaceWord.put("autoSetIdAfterInsert", autoSetIdAfterInsert.toString());
            replaceWord.put("insertListAutIncrementValue", insertListAutIncrementValue.toString());

            List<MethodBase> methodList = ParseMybatisXmlMethod.parser(tempClassFile.getAbsolutePath());
            List<MethodBase> methodRepeatList = ParseMybatisXmlMethod.tagRepeatMethod(methodList);
            ProcessWriteLine processWriteLine = new ProcessWriteLine(replaceWord, methodList, methodRepeatList);

            int currentLine = 0;
            while ((line = br.readLine()) != null) {
                currentLine++;
                processWriteLine.writeLineWithRepeatMethod(line, writer, currentLine);
            }
            br.close();

            writer.close();
            if (fileWriter != null) {
                fileWriter.close();
            }

            String bakPath = tempClassFile.getAbsolutePath() + ".bak";
            File bakFile = new File(bakPath);
            if (tempClassFile.exists()) {
                tempClassFile.delete();
            }
            if (bakFile.exists()) {
                bakFile.delete();
            }
            if (returnModel) {
                return stringWriter.toString();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

}
